from flask import Flask, jsonify, request
from flask_cors import CORS
from flask_socketio import SocketIO, emit
import paho.mqtt.client as mqtt
import threading
import json
from datetime import datetime
import struct
import base64
import os
from io import BytesIO
import time
import urllib.parse
from database import db_manager, SensorData, AlertLog, DeviceStatusHistory, ImageRecord

app = Flask(__name__)
app.config['SECRET_KEY'] = 'zhipei-xingyun-secret-key'
CORS(app)
socketio = SocketIO(app, cors_allowed_origins="*")

# MQTT配置
MQTT_BROKER = "47.109.75.129"
MQTT_PORT = 1883
SENSOR_DATA_TOPIC = "sensor/data"      # 传感器数据主题（接收）
CONTROL_TOPIC = "sensor/control"       # 控制指令主题（发送）
THRESHOLD_TOPIC = "sensor/threshold"   # 阈值设置主题（发送）
IMAGE_TOPIC = "image"                  # 图像分包主题
IMAGE_FINISH_TOPIC = "finish"          # 图像传输完成主题
MODE_CONTROL_TOPIC = "mode/control"    # 模式控制主题
PEST_DETECTION_TOPIC = "pest/detection"# 病虫害检测

# 全局变量
image_buffer = {}                      # 存储不同客户端的图像缓冲区
current_image = None                   # 当前显示的图像
stream_mode = True                     # True: 持续推流模式, False: 按需拍照模式
stream_paused = False                  # 推流是否暂停
image_receiving = {}  # 标记是否正在接收图像

# 存储传感器数据历史
sensor_data_history = {
    'co2': [], 'ph': [], 'tds': [], 'dissolved_oxygen': [],
    'temperature': [], 'humidity': [], 'light_intensity': [], 'pest_detection': []
}

# 最新传感器数据
current_sensor_data = {
    'co2': 0, 'ph': 0.0, 'tds': 0.0, 'dissolved_oxygen': 0.0,
    'temperature': 0.0, 'humidity': 0, 'light_intensity': 0.0,
    'pest_detection': 0, 'timestamp': ''
}

# 默认报警阈值设置
alert_thresholds = {
    'co2': {'min': 300, 'max': 1500},
    'ph': {'min': 5.5, 'max': 7.0},
    'tds': {'min': 800, 'max': 1500},
    'dissolved_oxygen': {'min': 5.0, 'max': 12.0},
    'temperature': {'min': 18.0, 'max': 28.0},
    'humidity': {'min': 50, 'max': 80},
    'light_intensity': {'min': 5000, 'max': 30000},
    'pest_detection': {'min': 0, 'max': 0}  # 病虫害检测，0为正常，1为异常
}

# 设备控制状态
device_status = {
    'grow_light': 0,      # 补光灯 (0-1)
    'alkaline_pump': 0,   # 碱性水泵 (0-1)
    'nutrient_pump': 0,   # 营养液泵 (0-1)
    'fan': 0,             # 风扇 (0-100)
    'heater': 0,          # 加热器 (0-1)
    'acid_pump': 0,       # 酸性水泵 (0-1)
    'humidifier': 0       # 加湿器 (0-1)
}

model_upload_status = {
    'in_progress': False,
    'paused': False,
    'cancelled': False,
    'current_chunk': 0,
    'total_chunks': 0,
    'model_data': None
}

# MQTT客户端实例
mqtt_client = None

def is_complete_jpeg(data):
    """
    检查数据是否构成完整的JPEG图像
    JPEG格式: 以0xFFD8开始，以0xFFD9结束
    """
    if len(data) < 4:  # 至少需要开始标记和结束标记
        return False
    
    # 检查开始标记
    if data[:2] != b"\xff\xd8":
        return False
    
    # 检查结束标记
    if data[-2:] != b"\xff\xd9":
        return False
    
    # 基本验证通过，可以认为是完整的JPEG
    return True

def extract_complete_jpegs(data):
    """
    从二进制数据中提取所有完整的JPEG图像
    返回: (完整图像列表, 剩余数据)
    """
    images = []
    remaining_data = bytearray(data)
    
    while len(remaining_data) >= 4:
        # 查找JPEG开始标记
        start_idx = remaining_data.find(b"\xff\xd8")
        if start_idx == -1:
            break  # 没有找到开始标记
        
        # 从开始标记位置查找结束标记
        end_idx = remaining_data.find(b"\xff\xd9", start_idx + 2)
        if end_idx == -1:
            break  # 没有找到结束标记
        
        # 提取完整的JPEG图像（包括开始和结束标记）
        jpeg_end = end_idx + 2  # 结束标记占2字节
        jpeg_data = bytes(remaining_data[start_idx:jpeg_end])
        
        # 验证提取的数据是否构成完整的JPEG
        if is_complete_jpeg(jpeg_data):
            images.append(jpeg_data)
            # 移除已提取的数据
            remaining_data = remaining_data[jpeg_end:]
        else:
            # 如果不是完整的JPEG，向前移动一个字节继续查找
            remaining_data = remaining_data[start_idx + 1:]
    
    return images, bytes(remaining_data)

def parse_sensor_data(json_data):
    """
    解析JSON格式的传感器数据
    格式:
    {
        "temp": 30,
        "huimi": 80,
        "CO2_Val": 500,
        "PH_Val": 7.8,
        "O2_Val": 101.5,
        "light_Val": 456.8,
        "TDS_Val": 12
    }
    """
    try:
        # 映射字段名到内部使用的字段名
        sensor_data = {
            'co2': json_data.get('CO2_Val', 0),
            'ph': json_data.get('PH_Val', 0.0),
            'tds': json_data.get('TDS_Val', 0.0),
            'dissolved_oxygen': json_data.get('O2_Val', 0.0),
            'temperature': json_data.get('temp', 0.0),
            'humidity': json_data.get('huimi', 0),
            'light_intensity': json_data.get('light_Val', 0.0)
        }
        
        # 数据验证和格式化
        for key, value in sensor_data.items():
            if value is None:
                print(f"警告: 传感器数据中缺少字段 {key}")
                return None
            
            # 确保数值类型正确
            try:
                if key in ['co2', 'humidity', 'pest_detection']:
                    sensor_data[key] = int(value)
                else:
                    sensor_data[key] = float(value)
            except (ValueError, TypeError):
                print(f"警告: 传感器数据字段 {key} 的值 {value} 无法转换为数字")
                return None
        
        return sensor_data
    except Exception as e:
        print(f"解析传感器数据错误: {e}")
        return None
        
def parse_pest_detection(payload):
    """
    解析病虫害检测结果
    内容: 字符串形式的整数，如 "0", "1", "2"
    """
    try:
        if isinstance(payload, bytes):
            payload = payload.decode('utf-8')
        
        # 去除可能的空白字符
        pest_value = str(payload).strip()
        
        # 转换为整数
        pest_detection = int(pest_value)
        
        # 验证值范围
        if pest_detection < 0:
            pest_detection = 0
            
        return pest_detection
    except (ValueError, TypeError) as e:
        print(f"解析病虫害检测数据错误: {e}, 原始数据: {payload}")
        return None
        
def handle_pest_detection(payload):
    """处理病虫害检测数据"""
    global current_sensor_data
    try:
        # 解析病虫害检测结果
        pest_detection = parse_pest_detection(payload)
        
        if pest_detection is not None:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            
            # 更新当前传感器数据中的病虫害检测值
            current_sensor_data['pest_detection'] = pest_detection
            current_sensor_data['timestamp'] = timestamp
            
            # 添加到历史数据
            sensor_data_history['pest_detection'].append(pest_detection)
            if len(sensor_data_history['pest_detection']) > 100:
                sensor_data_history['pest_detection'].pop(0)
            
            # 保存到数据库
            db_manager.save_sensor_data(current_sensor_data)
            
            # 检查警报
            alerts = check_alerts({'pest_detection': pest_detection})
            
            # 保存警报到数据库
            for alert in alerts:
                db_manager.save_alert(alert)
            
            # 在终端打印接收到的数据
            print(f"🐛 病虫害检测数据 [{timestamp}]:")
            print(f"  检测结果: {pest_detection}")
            print(f"  状态: {'✅ 正常' if pest_detection == 0 else '❌ 检测到病虫害'}")
            
            if alerts:
                print("⚠️  警报:")
                for alert in alerts:
                    print(f"  {alert['message']}")
            
            print("-" * 50)
            
            # 通过WebSocket将数据推送到前端
            socketio.emit('sensor_data', {
                'data': current_sensor_data,
                'alerts': alerts
            })
        else:
            print("❌ 解析病虫害检测数据失败")
            
    except Exception as e:
        print(f"❌ 处理病虫害检测数据时出错: {e}")

def build_threshold_packet(thresholds, packet_type):
    """
    构建阈值设置HEX数据包
    格式1: 0xFF 二氧化碳 pH值 TDS值 溶解氧 温度 湿度 光照 0xFF (下限)
    格式2: 0xFF 二氧化碳 pH值 TDS值 溶解氧 温度 湿度 光照 0xFE (上限)
    每个参数4字节
    """
    try:
        # 数据包头
        packet = bytearray([0xFF])
        
        # 添加各个参数 (每个4字节)
        parameters = ['co2', 'ph', 'tds', 'dissolved_oxygen', 'temperature', 'humidity', 'light_intensity']
        
        for param in parameters:
            value = thresholds[param]
            
            if param in ['co2', 'humidity']:  # 无符号整型
                packet.extend(value.to_bytes(4, byteorder='big', signed=False))
            elif param == 'temperature':  # 有符号整型
                packet.extend(value.to_bytes(4, byteorder='big', signed=True))
            else:  # 浮点型
                packet.extend(struct.pack('>f', value))
        
        # 数据包尾
        if packet_type == 'min':
            packet.append(0xFF)  # 下限包
        else:
            packet.append(0xFE)  # 上限包
            
        return packet
    except Exception as e:
        print(f"构建阈值数据包错误: {e}")
        return None

def build_control_packet(controls):
    """
    构建设备控制HEX数据包
    格式3: 0xFF 补光灯 碱性水泵 营养液泵 风扇 加热器 酸性水泵 加湿器 0xFE
    每个参数1字节 (0-255)
    """
    try:
        # 数据包头
        packet = bytearray([0xFF])
        
        # 添加各个设备状态 (每个1字节)
        devices = ['grow_light', 'alkaline_pump', 'nutrient_pump', 'fan', 'heater', 'acid_pump', 'humidifier']
        
        for device in devices:
            value = controls[device]
            # 确保值在0-255范围内
            packet.append(max(0, min(255, value)))
        
        # 数据包尾
        packet.append(0xFE)
        
        return packet
    except Exception as e:
        print(f"构建控制数据包错误: {e}")
        return None

def check_alerts(sensor_data):
    """检查传感器数据是否超出阈值"""
    alerts = []
    for param, value in sensor_data.items():
        if param in alert_thresholds:
            thresholds = alert_thresholds[param]
            
            # 特殊处理病虫害检测
            if param == 'pest_detection':
                print(f"当前检测到病叶的数量为：{value}")
                # if value != 0:  # 检测到病虫害
                #     alerts.append({
                #         'parameter': param,
                #         'value': value,
                #         'threshold': 0,
                #         'type': 'pest',
                #         'message': '⚠️ 检测到病虫害！请及时处理'
                #     })
                continue
                
            # 其他参数的正常阈值检查
            if value < thresholds['min']:
                alerts.append({
                    'parameter': param,
                    'value': value,
                    'threshold': thresholds['min'],
                    'type': 'low',
                    'message': f"{param}值过低: {value} < {thresholds['min']}"
                })
            elif value > thresholds['max']:
                alerts.append({
                    'parameter': param,
                    'value': value,
                    'threshold': thresholds['max'],
                    'type': 'high',
                    'message': f"{param}值过高: {value} > {thresholds['max']}"
                })
    return alerts
    
def validate_image_quality(image_bytes):
    """验证图像质量"""
    try:
        # 检查文件大小
        if len(image_bytes) < 1024:  # 小于1KB的图像可能有问题
            print("⚠️  图像文件过小，可能损坏")
            return False
        
        # 检查JPEG文件头
        if image_bytes[0:2] != b'\xff\xd8':
            print("⚠️  无效的JPEG文件头")
            return False
        
        # 检查JPEG文件尾
        if image_bytes[-2:] != b'\xff\xd9':
            print("⚠️  图像文件可能不完整")
            return False
        
        return True
    except Exception as e:
        print(f"❌ 图像质量检查失败: {e}")
        return False

def save_image_to_file(image_bytes, filename=None):
    """将图像字节数据保存到文件（用于调试）"""
    try:
        if filename is None:
            filename = f"capture_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
        
        with open(filename, 'wb') as f:
            f.write(image_bytes)
        print(f"💾 图像已保存到: {filename}")
        return True
    except Exception as e:
        print(f"❌ 保存图像失败: {e}")
        return False

def handle_image_chunk(payload):
    """处理图像分包数据（二进制），自动检测完整JPEG图像"""
    try:
        # 简单的客户端标识
        client_id = "default"
        
        # 初始化缓冲区（使用字节数组）
        if client_id not in image_buffer:
            image_buffer[client_id] = bytearray()
        
        # 直接追加二进制数据到缓冲区
        if isinstance(payload, bytes):
            image_buffer[client_id].extend(payload)
        else:
            # 如果收到的是字符串，尝试编码为字节
            image_buffer[client_id].extend(payload.encode('utf-8'))
        
        print(f"📷 接收到图像分包，缓冲区大小: {len(image_buffer[client_id])} 字节")
        
        # 尝试从缓冲区中提取完整的JPEG图像
        buffer_data = bytes(image_buffer[client_id])
        complete_images, remaining_data = extract_complete_jpegs(buffer_data)
        
        # 处理所有提取到的完整图像
        for image_data in complete_images:
            process_complete_image(image_data)
        
        # 更新缓冲区，只保留未处理的数据
        image_buffer[client_id] = bytearray(remaining_data)
        print(f"📷 缓冲区更新后大小: {len(image_buffer[client_id])} 字节")
        
    except Exception as e:
        print(f"❌ 处理图像分包失败: {e}")
        
def process_complete_image(image_data):
    """处理完整的JPEG图像"""
    global stream_paused
    
    try:
        print(f"🖼️  检测到完整JPEG图像，大小: {len(image_data)} 字节")
        
        # 验证图像数据
        if len(image_data) < 100:  # 最小图像大小检查
            print("❌ 图像数据过小，可能不完整")
            return
        
        # 将二进制数据转换为base64字符串用于前端显示
        base64_data = base64.b64encode(image_data).decode('utf-8')
        
        # 更新全局当前图像（base64格式，用于前端显示）
        global current_image
        current_image = base64_data
        
        # 保存图像到数据库（base64格式）
        db_manager.save_image_record(base64_data)
        
        # 通过WebSocket发送图像到前端（base64格式）
        socketio.emit('image_update', {
            'image_data': f"data:image/jpeg;base64,{base64_data}",
            'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })
        
        print("✅ 图像处理完成并发送到前端")
        
        # 根据当前模式决定是否发送continue指令
        if stream_mode and not stream_paused:
            # 持续推流模式且未暂停，发送continue指令
            if mqtt_client and mqtt_client.is_connected():
                mqtt_client.publish("finish", "continue")
                print("📤 已发送continue指令到4G模块")
        elif not stream_mode:
            # 按需拍照模式，拍照完成后自动暂停
            stream_paused = True
            print("📸 拍照完成，设备返回休眠状态")
            
            # 通知前端模式状态更新
            socketio.emit('mode_update', {
                'stream_mode': stream_mode,
                'stream_paused': stream_paused,
                'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            })
            
    except Exception as e:
        print(f"❌ 处理完整图像失败: {e}")

def on_mqtt_connect(client, userdata, flags, rc):
    """MQTT连接回调函数"""
    if rc == 0:
        print("✅ 成功连接到MQTT服务器!")
        client.subscribe(SENSOR_DATA_TOPIC)
        client.subscribe(IMAGE_TOPIC)        
        client.subscribe(IMAGE_FINISH_TOPIC)
        client.subscribe(MODE_CONTROL_TOPIC)
        client.subscribe(PEST_DETECTION_TOPIC)
        print(f"✅ 已订阅主题: {SENSOR_DATA_TOPIC}, {IMAGE_TOPIC}, {IMAGE_FINISH_TOPIC}, {MODE_CONTROL_TOPIC}, {PEST_DETECTION_TOPIC}")
    else:
        print(f"❌ 连接失败，错误代码: {rc}")

def on_mqtt_message(client, userdata, msg):
    """MQTT消息接收回调函数"""
    global current_sensor_data
    try:
        if msg.topic == SENSOR_DATA_TOPIC:
            # 解析JSON数据（不含病虫害检测）
            try:
                # 尝试解析JSON
                json_data = json.loads(msg.payload)
                print(f"📊 收到JSON传感器数据: {json_data}")
                
                # 解析传感器数据
                sensor_data = parse_sensor_data(json_data)
                
                if sensor_data:
                    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    
                    # 保留当前的病虫害检测值
                    pest_detection = current_sensor_data.get('pest_detection', 0)
                    
                    # 更新当前数据（保留病虫害检测值）
                    sensor_data['pest_detection'] = pest_detection
                    sensor_data['timestamp'] = timestamp
                    current_sensor_data = sensor_data
                    
                    # 保存到数据库
                    db_manager.save_sensor_data(sensor_data)
                    
                    # 添加到历史数据 (保留最近100条)
                    for key in sensor_data_history:
                        if key in sensor_data and key != 'timestamp':
                            sensor_data_history[key].append(sensor_data[key])
                            if len(sensor_data_history[key]) > 100:
                                sensor_data_history[key].pop(0)
                    
                    # 检查警报
                    alerts = check_alerts(sensor_data)
                    
                    # 保存警报到数据库
                    for alert in alerts:
                        db_manager.save_alert(alert)
                    
                    # 在终端打印接收到的数据
                    print(f"📊 传感器数据 [{timestamp}]:")
                    print(f"  二氧化碳: {sensor_data['co2']} ppm")
                    print(f"  pH值: {sensor_data['ph']}")
                    print(f"  TDS值: {sensor_data['tds']} ppm")
                    print(f"  溶解氧: {sensor_data['dissolved_oxygen']} mg/L")
                    print(f"  温度: {sensor_data['temperature']} °C")
                    print(f"  湿度: {sensor_data['humidity']} %")
                    print(f"  光照强度: {sensor_data['light_intensity']} lux")
                    print(f"  病虫害检测: {'✅ 正常' if sensor_data['pest_detection'] == 0 else '❌ 检测到病虫害'}")
                    
                    if alerts:
                        print("⚠️  警报:")
                        for alert in alerts:
                            print(f"  {alert['message']}")
                    
                    print("-" * 50)
                    
                    # 通过WebSocket将数据推送到前端
                    socketio.emit('sensor_data', {
                        'data': sensor_data,
                        'alerts': alerts
                    })
                else:
                    print("❌ 解析传感器数据失败")
                    
            except json.JSONDecodeError:
                print("❌ 传感器数据不是有效的JSON格式")
                print(f"原始数据: {msg.payload}")
            except Exception as e:
                print(f"❌ 处理传感器数据时出错: {e}")
                
        elif msg.topic == PEST_DETECTION_TOPIC:
            # 处理病虫害检测数据
            handle_pest_detection(msg.payload)
                
        elif msg.topic == IMAGE_TOPIC:
            # 处理图像分包数据
            handle_image_chunk(msg.payload)
            
        elif msg.topic == MODE_CONTROL_TOPIC:
            # 处理模式控制消息
            handle_mode_control(msg.payload)
            
    except Exception as e:
        print(f"❌ 处理MQTT消息时出错: {e}")

def on_mqtt_disconnect(client, userdata, rc):
    """MQTT断开连接回调函数"""
    print("⚠️  MQTT连接已断开")
    if rc != 0:
        print("意外断开连接，尝试重连...")
        try:
            client.reconnect()
        except:
            print("重连失败")

def connect_mqtt():
    """连接MQTT服务器"""
    global mqtt_client
    
    mqtt_client = mqtt.Client()
    mqtt_client.on_connect = on_mqtt_connect
    mqtt_client.on_message = on_mqtt_message
    mqtt_client.on_disconnect = on_mqtt_disconnect
    
    try:
        mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
        # 在后台线程中运行MQTT客户端
        mqtt_thread = threading.Thread(target=mqtt_client.loop_forever)
        mqtt_thread.daemon = True
        mqtt_thread.start()
        print("🚀 MQTT客户端已启动")
        return True
    except Exception as e:
        print(f"❌ 连接MQTT服务器失败: {e}")
        return False

def handle_mode_control(payload):
    """处理模式控制指令"""
    global stream_mode, stream_paused
    
    try:
        if isinstance(payload, bytes):
            command = payload.decode('utf-8').strip().lower()
        else:
            command = str(payload).strip().lower()
        
        print(f"📝 收到模式控制指令: {command}")
        
        if command == "stream":
            # 切换到持续推流模式
            stream_mode = True
            stream_paused = False
            print("🎥 切换到持续推流模式")
            
        elif command == "on_demand":
            # 切换到按需拍照模式
            stream_mode = False
            stream_paused = True
            print("📸 切换到按需拍照模式，设备进入休眠")
            
        elif command == "capture":
            # 请求拍照（在按需模式下）
            if not stream_mode:
                stream_paused = False
                print("📸 唤醒设备进行拍照")
                
        elif command == "pause":
            # 暂停推流（在持续模式下）
            if stream_mode:
                stream_paused = True
                print("⏸️ 暂停持续推流")
                
        elif command == "resume":
            # 恢复推流（在持续模式下）
            if stream_mode:
                stream_paused = False
                print("▶️ 恢复持续推流")
        
        # 通过WebSocket通知前端模式状态更新
        socketio.emit('mode_update', {
            'stream_mode': stream_mode,
            'stream_paused': stream_paused,
            'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })
        
    except Exception as e:
        print(f"❌ 处理模式控制指令失败: {e}")

# Flask路由
@app.route('/')
def home():
    return jsonify({
        "message": "智培星云——分布式水培生态中枢服务运行中!", 
        "status": "success",
        "mqtt_broker": MQTT_BROKER,
        "topic": SENSOR_DATA_TOPIC
    })

@app.route('/api/current-data', methods=['GET'])
def get_current_data():
    """获取当前传感器数据"""
    return jsonify({
        "status": "success",
        "data": current_sensor_data
    })

@app.route('/api/history-data', methods=['GET'])
def get_history_data():
    """获取传感器历史数据"""
    return jsonify({
        "status": "success",
        "data": sensor_data_history
    })

@app.route('/api/thresholds', methods=['GET', 'POST'])
def handle_thresholds():
    """获取或设置报警阈值"""
    if request.method == 'GET':
        return jsonify({
            "status": "success",
            "data": alert_thresholds
        })
    else:
        data = request.json
        for param, thresholds in data.items():
            if param in alert_thresholds:
                alert_thresholds[param] = thresholds
        
        # 保存阈值到数据库
        db_manager.set_system_setting(
            'alert_thresholds', 
            json.dumps(alert_thresholds),
            '报警阈值设置'
        )
        
        # 发送阈值到设备
        send_thresholds_to_device()
        
        return jsonify({"status": "success", "message": "阈值已更新"})

@app.route('/api/device-status', methods=['GET'])
def get_device_status():
    """获取设备状态"""
    return jsonify({
        "status": "success",
        "data": device_status
    })

@app.route('/api/control-device', methods=['POST'])
def control_device():
    """控制单个设备"""
    data = request.json
    device = data.get('device', '')
    value = data.get('value', 0)
    
    if device not in device_status:
        return jsonify({"status": "error", "message": "设备不存在"}), 400
    
    # 验证值范围
    if device == 'fan':  # 风扇支持0-100
        if value < 0 or value > 100:
            return jsonify({"status": "error", "message": "风扇速度应在0-100之间"}), 400
    else:  # 其他设备只支持0或1
        if value not in [0, 1]:
            return jsonify({"status": "error", "message": "设备状态应为0或1"}), 400
    
    # 保存设备状态变化到数据库
    if device_status[device] != value:
        db_manager.save_device_status(device, value)
    
    # 更新设备状态
    device_status[device] = value
    
    # 发送控制指令到设备
    success = send_control_to_device()
    
    if success:
        return jsonify({
            "status": "success", 
            "message": f"{device} 已设置为 {value}",
            "data": device_status
        })
    else:
        return jsonify({"status": "error", "message": "发送控制指令失败"}), 500

@app.route('/api/control-multiple', methods=['POST'])
def control_multiple_devices():
    """批量控制设备"""
    data = request.json
    controls = data.get('controls', {})
    
    # 验证并更新设备状态
    for device, value in controls.items():
        if device in device_status:
            if device == 'fan':  # 风扇支持0-100
                if 0 <= value <= 100:
                    device_status[device] = value
            else:  # 其他设备只支持0或1
                if value in [0, 1]:
                    device_status[device] = value
    
    # 发送控制指令到设备
    success = send_control_to_device()
    
    if success:
        return jsonify({
            "status": "success", 
            "message": "设备控制指令已发送",
            "data": device_status
        })
    else:
        return jsonify({"status": "error", "message": "发送控制指令失败"}), 500

@app.route('/api/current-image', methods=['GET'])
def get_current_image():
    """获取当前图像"""
    global current_image
    if current_image:
        return jsonify({
            "status": "success",
            "image_data": f"data:image/jpeg;base64,{current_image}",
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })
    else:
        return jsonify({
            "status": "error",
            "message": "暂无图像数据"
        }), 404

@app.route('/api/mode-status', methods=['GET'])
def get_mode_status():
    """获取当前模式状态"""
    return jsonify({
        "status": "success",
        "stream_mode": stream_mode,
        "stream_paused": stream_paused
    })

@app.route('/api/switch-mode', methods=['POST'])
def switch_mode():
    """切换图像传输模式"""
    global stream_mode, stream_paused
    
    data = request.json
    mode = data.get('mode', 'stream')  # stream 或 on_demand
    
    if mode == 'stream':
        stream_mode = True
        stream_paused = False
        # 发送模式切换指令到设备
        if mqtt_client and mqtt_client.is_connected():
            mqtt_client.publish(IMAGE_FINISH_TOPIC, "continue")
        return jsonify({
            "status": "success", 
            "message": "已切换到持续推流模式",
            "stream_mode": stream_mode,
            "stream_paused": stream_paused
        })
        
    elif mode == 'on_demand':
        stream_mode = False
        stream_paused = True
        # 发送模式切换指令到设备
        if mqtt_client and mqtt_client.is_connected():
            mqtt_client.publish(MODE_CONTROL_TOPIC, "on_demand")
        return jsonify({
            "status": "success", 
            "message": "已切换到按需拍照模式",
            "stream_mode": stream_mode,
            "stream_paused": stream_paused
        })
    
    else:
        return jsonify({"status": "error", "message": "无效的模式"}), 400

@app.route('/api/control-stream', methods=['POST'])
def control_stream():
    """控制推流状态（暂停/恢复）"""
    global stream_paused
    
    data = request.json
    action = data.get('action', '')  # pause 或 resume
    
    if action == 'pause' and not stream_paused:
        stream_paused = True
        if mqtt_client and mqtt_client.is_connected():
            mqtt_client.publish(MODE_CONTROL_TOPIC, "pause")
        return jsonify({
            "status": "success", 
            "message": "已暂停推流",
            "stream_paused": stream_paused
        })
        
    elif action == 'resume' and stream_paused:
        stream_paused = False
        if mqtt_client and mqtt_client.is_connected():
            mqtt_client.publish(IMAGE_FINISH_TOPIC, "continue")
        return jsonify({
            "status": "success", 
            "message": "已恢复推流",
            "stream_paused": stream_paused
        })
    
    else:
        return jsonify({"status": "error", "message": "无效的操作或状态"}), 400

@app.route('/api/request-capture', methods=['POST'])
def request_capture():
    """在按需模式下请求拍照"""
    global stream_paused
    
    if stream_mode:
        return jsonify({"status": "error", "message": "当前为持续推流模式，无需单独请求拍照"}), 400
    
    # 唤醒设备进行拍照
    stream_paused = False
    if mqtt_client and mqtt_client.is_connected():
        mqtt_client.publish(IMAGE_FINISH_TOPIC, "continue")
    
    return jsonify({
        "status": "success", 
        "message": "已发送拍照请求",
        "stream_paused": stream_paused
    })

def send_thresholds_to_device():
    """发送阈值设置到设备"""
    if not mqtt_client or not mqtt_client.is_connected():
        return False
    
    try:
        # 发送下限阈值 (格式1)
        min_packet = build_threshold_packet(
            {k: v['min'] for k, v in alert_thresholds.items() if k != 'pest_detection'}, 
            'min'
        )
        if min_packet:
            mqtt_client.publish(THRESHOLD_TOPIC, min_packet)
            print("📤 已发送下限阈值数据包")
        
        # 发送上限阈值 (格式2)
        max_packet = build_threshold_packet(
            {k: v['max'] for k, v in alert_thresholds.items() if k != 'pest_detection'}, 
            'max'
        )
        if max_packet:
            mqtt_client.publish(THRESHOLD_TOPIC, max_packet)
            print("📤 已发送上限阈值数据包")
        
        return True
    except Exception as e:
        print(f"发送阈值数据包失败: {e}")
        return False

def send_control_to_device():
    """发送设备控制指令到设备"""
    if not mqtt_client or not mqtt_client.is_connected():
        return False
    
    try:
        control_packet = build_control_packet(device_status)
        if control_packet:
            mqtt_client.publish(CONTROL_TOPIC, control_packet)
            print("📤 已发送设备控制数据包")
            print(f"  设备状态: {device_status}")
            return True
    except Exception as e:
        print(f"发送控制数据包失败: {e}")
    
    return False
    
@app.route('/api/database/sensor-history', methods=['GET'])
def get_sensor_history():
    """获取传感器历史数据"""
    try:
        hours = request.args.get('hours', 24, type=int)
        limit = request.args.get('limit', 1000, type=int)
        
        history = db_manager.get_sensor_data_history(hours=hours, limit=limit)
        
        return jsonify({
            "status": "success",
            "data": history,
            "count": len(history)
        })
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/database/alerts', methods=['GET'])
def get_alerts_history():
    """获取警报历史"""
    try:
        unresolved_only = request.args.get('unresolved_only', 'false').lower() == 'true'
        hours = request.args.get('hours', 24, type=int)
        limit = request.args.get('limit', 100, type=int)
        
        alerts = db_manager.get_alerts(
            unresolved_only=unresolved_only,
            hours=hours,
            limit=limit
        )
        
        return jsonify({
            "status": "success",
            "data": alerts,
            "count": len(alerts)
        })
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/database/resolve-alert/<int:alert_id>', methods=['POST'])
def resolve_alert(alert_id):
    """解决警报"""
    try:
        success = db_manager.resolve_alert(alert_id)
        if success:
            return jsonify({"status": "success", "message": "警报已解决"})
        else:
            return jsonify({"status": "error", "message": "警报不存在"}), 404
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/database/device-history', methods=['GET'])
def get_device_history():
    """获取设备状态历史"""
    try:
        device = request.args.get('device', '')
        hours = request.args.get('hours', 24, type=int)
        limit = request.args.get('limit', 100, type=int)
        
        with db_manager.app.app_context():
            from datetime import datetime, timedelta
            time_threshold = datetime.now() - timedelta(hours=hours)
            
            query = DeviceStatusHistory.query.filter(
                DeviceStatusHistory.timestamp >= time_threshold
            )
            
            if device:
                query = query.filter(DeviceStatusHistory.device == device)
            
            records = query.order_by(DeviceStatusHistory.timestamp.desc()).limit(limit).all()
            history = [record.to_dict() for record in records]
            
            return jsonify({
                "status": "success",
                "data": history,
                "count": len(history)
            })
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/database/image-history', methods=['GET'])
def get_image_history():
    """获取图像历史"""
    try:
        hours = request.args.get('hours', 24, type=int)
        limit = request.args.get('limit', 50, type=int)
        
        with db_manager.app.app_context():
            from datetime import datetime, timedelta
            time_threshold = datetime.now() - timedelta(hours=hours)
            
            records = ImageRecord.query.filter(
                ImageRecord.timestamp >= time_threshold
            ).order_by(ImageRecord.timestamp.desc()).limit(limit).all()
            
            history = [record.to_dict() for record in records]
            
            return jsonify({
                "status": "success",
                "data": history,
                "count": len(history)
            })
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500
        
@app.route('/api/upload-model', methods=['POST'])
def upload_model():
    """上传神经网络模型文件"""
    global model_upload_status
    
    try:
        # 检查是否已有上传在进行中
        if model_upload_status['in_progress'] and not model_upload_status['cancelled']:
            return jsonify({"status": "error", "message": "已有上传任务在进行中"}), 400
        
        # 重置上传状态
        model_upload_status.update({
            'in_progress': True,
            'paused': False,
            'cancelled': False,
            'current_chunk': 0,
            'total_chunks': 0,
            'model_data': None
        })
        
        # 获取文件信息
        file_name = request.headers.get('X-File-Name')
        file_size = request.headers.get('X-File-Size')
        
        if not file_name or not file_size:
            return jsonify({"status": "error", "message": "缺少文件信息"}), 400
        
        # 解码文件名
        file_name = urllib.parse.unquote(file_name)
        
        # 获取二进制数据
        model_data = request.get_data()
        
        if not model_data:
            return jsonify({"status": "error", "message": "没有接收到文件数据"}), 400
        
        print(f"📤 接收到模型文件: {file_name}, 大小: {len(model_data)} 字节")
        
        # 保存模型数据到全局状态
        model_upload_status['model_data'] = model_data
        
        # 发送模型数据到本地端
        success = send_model_to_device(model_data)
        
        if success:
            # 重置上传状态
            model_upload_status.update({
                'in_progress': False,
                'paused': False,
                'cancelled': False,
                'current_chunk': 0,
                'total_chunks': 0,
                'model_data': None
            })
            
            return jsonify({
                "status": "success", 
                "message": f"模型文件 {file_name} 已成功发送到本地端",
                "file_size": len(model_data)
            })
        else:
            return jsonify({"status": "error", "message": "发送模型文件到本地端失败"}), 500
            
    except Exception as e:
        print(f"❌ 上传模型文件失败: {e}")
        # 重置上传状态
        model_upload_status.update({
            'in_progress': False,
            'paused': False,
            'cancelled': False,
            'current_chunk': 0,
            'total_chunks': 0,
            'model_data': None
        })
        return jsonify({"status": "error", "message": f"上传失败: {str(e)}"}), 500

@app.route('/api/pause-upload', methods=['POST'])
def pause_upload():
    """暂停模型上传"""
    global model_upload_status
    
    if not model_upload_status['in_progress']:
        return jsonify({"status": "error", "message": "没有正在进行的上传任务"}), 400
    
    if model_upload_status['paused']:
        return jsonify({"status": "error", "message": "上传已经暂停"}), 400
    
    model_upload_status['paused'] = True
    
    # 通知前端上传已暂停
    socketio.emit('model_upload_paused', {
        'current_chunk': model_upload_status['current_chunk'],
        'total_chunks': model_upload_status['total_chunks']
    })
    
    return jsonify({
        "status": "success", 
        "message": "上传已暂停",
        "current_chunk": model_upload_status['current_chunk'],
        "total_chunks": model_upload_status['total_chunks']
    })

@app.route('/api/resume-upload', methods=['POST'])
def resume_upload():
    """恢复模型上传"""
    global model_upload_status
    
    if not model_upload_status['in_progress']:
        return jsonify({"status": "error", "message": "没有正在进行的上传任务"}), 400
    
    if not model_upload_status['paused']:
        return jsonify({"status": "error", "message": "上传没有暂停"}), 400
    
    model_upload_status['paused'] = False
    
    # 通知前端上传已恢复
    current_progress = int((model_upload_status['current_chunk'] + 1) / model_upload_status['total_chunks'] * 100)
    
    socketio.emit('model_upload_resumed', {
        'progress': current_progress,
        'current_chunk': model_upload_status['current_chunk'],
        'total_chunks': model_upload_status['total_chunks']
    })
    
    return jsonify({
        "status": "success", 
        "message": "上传已恢复",
        "current_chunk": model_upload_status['current_chunk'],
        "total_chunks": model_upload_status['total_chunks']
    })


@app.route('/api/cancel-upload', methods=['POST'])
def cancel_upload():
    """取消模型上传"""
    global model_upload_status
    
    if not model_upload_status['in_progress']:
        return jsonify({"status": "error", "message": "没有正在进行的上传任务"}), 400
    
    model_upload_status.update({
        'cancelled': True,
        'in_progress': False,
        'paused': False
    })
    
    # 通知前端上传已取消
    socketio.emit('model_upload_cancelled')
    
    # 发送取消信号到本地端
    if mqtt_client and mqtt_client.is_connected():
        mqtt_client.publish("finish", "upload cancelled")
    
    return jsonify({"status": "success", "message": "上传已取消"})

def send_model_to_device(model_data):
    """发送模型文件到本地端"""
    global model_upload_status
    
    if not mqtt_client or not mqtt_client.is_connected():
        print("❌ MQTT未连接，无法发送模型文件")
        return False
    
    try:
        # 分包发送模型数据（每包16B）
        chunk_size = 16
        total_size = len(model_data)
        num_chunks = (total_size + chunk_size - 1) // chunk_size
        
        model_upload_status['total_chunks'] = num_chunks
        
        print(f"📦 开始发送模型数据，共 {num_chunks} 包")
        
        # 发送初始进度
        socketio.emit('model_upload_progress', {'progress': 0})
        
        for i in range(num_chunks):
            # 检查是否暂停
            while model_upload_status['paused'] and not model_upload_status['cancelled']:
               sleep(0.1)  # 等待恢复
            
            # 检查是否取消
            if model_upload_status['cancelled']:
                print("❌ 上传已被取消")
                return False
                
            start = i * chunk_size
            end = min(start + chunk_size, total_size)
            chunk = model_data[start:end]
            
            # 更新当前块
            model_upload_status['current_chunk'] = i
            
            # 发送数据包
            mqtt_client.publish("model/update", chunk)
            
            # 计算并发送进度
            progress = int((i + 1) / num_chunks * 100)
            socketio.emit('model_upload_progress', {'progress': progress})
            print(f"📤 发送进度: {progress}%")
            
            # 添加小延迟避免堵塞
            time.sleep(0.1)
        
        print("✅ 模型数据发送完成")
        
        # 发送完成信号
        mqtt_client.publish("finish", "update over")
        print("📤 已发送模型更新完成信号")
        
        # 发送最终进度
        socketio.emit('model_upload_progress', {'progress': 100})
        
        return True
        
    except Exception as e:
        print(f"❌ 发送模型文件失败: {e}")
        # 发送错误进度
        socketio.emit('model_upload_progress', {'progress': -1, 'error': str(e)})
        return False

# WebSocket事件
@socketio.on('connect')
def handle_connect():
    print('✅ 前端客户端已连接')
    emit('connected', {'data': '连接成功'})

@socketio.on('disconnect')
def handle_disconnect():
    print('❌ 前端客户端已断开连接')

@socketio.on('get_device_status')
def handle_get_device_status():
    """处理前端获取设备状态的请求"""
    emit('device_status_update', device_status)

@socketio.on('request_image')
def handle_request_image():
    """处理前端请求当前图像"""
    global current_image
    if current_image:
        emit('image_update', {
            'image_data': f"data:image/jpeg;base64,{current_image}",
            'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })

@socketio.on('switch_mode')
def handle_switch_mode(data):
    """处理前端切换模式的请求"""
    mode = data.get('mode', 'stream')
    
    if mode == 'stream':
        switch_mode()
    elif mode == 'on_demand':
        switch_mode()
    else:
        emit('mode_update', {
            'status': 'error',
            'message': '无效的模式'
        })

@socketio.on('control_stream')
def handle_control_stream(data):
    """处理前端控制推流的请求"""
    action = data.get('action', '')
    
    if action in ['pause', 'resume']:
        control_stream()
    else:
        emit('mode_update', {
            'status': 'error',
            'message': '无效的操作'
        })

@socketio.on('request_capture')
def handle_request_capture():
    """处理前端请求拍照的请求"""
    request_capture()
    
def load_system_settings():
    """加载系统设置"""
    try:
        # 从数据库加载阈值设置
        thresholds_json = db_manager.get_system_setting('alert_thresholds')
        if thresholds_json:
            global alert_thresholds
            alert_thresholds = json.loads(thresholds_json)
            print("✅ 已从数据库加载阈值设置")
    except Exception as e:
        print(f"❌ 加载系统设置失败: {e}")
        
@socketio.on('pause_upload')
def handle_pause_upload():
    """处理前端暂停上传请求"""
    pause_upload()

@socketio.on('resume_upload')
def handle_resume_upload():
    """处理前端恢复上传请求"""
    resume_upload()

@socketio.on('cancel_upload')
def handle_cancel_upload():
    """处理前端取消上传请求"""
    cancel_upload()

# 应用启动时初始化
def initialize_app():
    """应用初始化函数"""
    print("🔧 初始化数据库...")
    # 初始化数据库
    db_manager.init_app(app)
    
    print("🔧 初始化MQTT连接...")
    connect_mqtt()
    
    # 加载系统设置
    print("🔧 加载系统设置...")
    load_system_settings()

# 手动初始化调用
initialize_app()

if __name__ == '__main__':
    print("🚀 启动智培星云监控系统...")
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)